home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / newsgrp / group96b.txt / 000042_icon-group-sender _Fri Oct 25 11:00:09 1996.msg < prev    next >
Internet Message Format  |  1997-01-02  |  9KB

  1. Received: by cheltenham.cs.arizona.edu; Fri, 25 Oct 1996 12:58:04 MST
  2. Date: Fri, 25 Oct 1996 11:00:09 -0700
  3. From: Gregg Townsend <gmt>
  4. Message-Id: <9610251800.AA12975@hawk.CS.Arizona.EDU>
  5. To: nr@viper.cs.Virginia.EDU
  6. Subject: Re:  Icon code for working with HTML
  7. Cc: icon-group
  8. Errors-To: icon-group-errors@cs.arizona.edu
  9.  
  10. Here's some code for parsing HTML files.  It isn't exactly what
  11. you're looking for, but it may help.
  12.  
  13.    Gregg Townsend / gmt@CS.Arizona.EDU / +1 520 621 4325 / 32 13 45N 110 57 16W
  14.    Computer Science / Univ of Arizona / 1040 E 4th St / Tucson AZ 85721-0077
  15.  
  16.  
  17. ############################################################################
  18. #
  19. #    File:     html.icn
  20. #
  21. #    Subject:  Procedures for parsing HTML
  22. #
  23. #    Author:   Gregg M. Townsend
  24. #
  25. #    Date:     August 12, 1996
  26. #
  27. ############################################################################
  28. #
  29. #    These procedures parse HTML files:
  30. #
  31. #    htchunks(f)    generates the basic chunks -- tags and text --
  32. #            that compose an HTML file.
  33. #
  34. #    htrefs(f)    generates the tagname/keyword/value combinations
  35. #            that reference other files.
  36. #
  37. #    These procedures process strings from HTML files:
  38. #
  39. #    httag(s)    extracts the name of a tag.
  40. #
  41. #    htvals(s)    generates the keyword/value pairs from a tag.
  42. #
  43. #    urlmerge(base,new) interprets a new URL in the context of a base.
  44. #
  45. ############################################################################
  46. #
  47. #       htchunks(f) generates the HTML chunks from file f.
  48. #    It returns strings beginning with
  49. #
  50. #        <!--    for unclosed comments (legal comments are deleted)
  51. #        <    for tags (will end with ">" unless unclosed at EOF)
  52. #    anything else    for text
  53. #
  54. #    At this level entities such as & are left unprocessed and all
  55. #    whitespace is preserved, including newlines.
  56. #
  57. ############################################################################
  58. #
  59. #    htrefs(f) extracts file/url references from within an HTML file
  60. #    and generates a string of the form
  61. #        tagname keyword value
  62. #       for each refrerence.
  63. #
  64. #    A single space character separates the three fields, but if no
  65. #    value is supplied for the keyword, no space follows the keyword.
  66. #    Tag and keyword names are always returned in upper case.
  67. #
  68. #    Quotation marks are stripped from the value, but note that the
  69. #    value can contain spaces or other special characters (although
  70. #    by strict HTML rules it probably shouldn't).
  71. #
  72. #       A table in the code determines which fields are references to
  73. #    other files.  For example, with <IMG>, SRC= is a reference but
  74. #    WIDTH= is not.  The table will probably never be perfect
  75. #    considering the mutation rate of HTML in actual practice.
  76. #
  77. ############################################################################
  78. #
  79. #    httag(s) extracts and returns the tag name from within an HTML
  80. #    tag string of the form "<tagname...>".   The tag name is returned
  81. #    in upper case.
  82. #
  83. ############################################################################
  84. #
  85. #    htvals(s) generates the tag values contained within an HTML tag
  86. #    string of the form "<tagname kw=val kw=val ...>".   For each
  87. #    keyword=value pair beyond the tagname, a string of the form
  88. #
  89. #        keyword value
  90. #
  91. #    is generated.  One space follows the keyword, which is returned
  92. #    in upper case, and quotation marks are stripped from the value.
  93. #    The value itself can be an empty string.
  94. #
  95. #    For each keyword given without a value, the keyword is generated
  96. #    in upper case with no following space.
  97. #
  98. #    Parsing is somewhat tolerant of errors.
  99. #
  100. ############################################################################
  101. #
  102. #    urlmerge(base,new) interprets a full or partial new URL in the
  103. #    context of a base URL, returning the combined URL.
  104. #
  105. #    Here are some examples of applying urlmerge() with a base value
  106. #    of "http://www.vcu.edu/misc/sched.html" and a new value as given:
  107. #
  108. #    new        result
  109. #    -------------    -------------------
  110. #    #tuesday    http://www.vcu.edu/misc/sched.html#tuesday
  111. #    bulletin.html    http://www.vcu.edu/misc/bulletin.html
  112. #    ./results.html    http://www.vcu.edu/misc/results.html
  113. #    images/rs.gif    http://www.vcu.edu/misc/images/rs.gif
  114. #    ../        http://www.vcu.edu/
  115. #    /greet.html    http://www.vcu.edu/greet.html
  116. #    file:a.html    file:a.html
  117. #
  118. #    Path components of "./" and "../" at the beginning of the
  119. #    new URL are handled specially to produce a simpler result.
  120. #    No other simplifications are applied.
  121. #
  122. ############################################################################
  123.  
  124.  
  125. #   htchunks(f) -- generate HTML chunks from file f
  126.  
  127. procedure htchunks(f)            #: generate chunks of HTML file
  128.    local prev, line, s
  129.  
  130.    "" ? repeat {
  131.  
  132.       if pos(0) then
  133.      &subject := (read(f) || "\n") | fail
  134.  
  135.       if ="<!--" then
  136.      suspend htc_comment(f)        # fails if comment is legal
  137.       else if ="<" then
  138.      suspend htc_tag(f)        # generate tag
  139.       else
  140.      suspend htc_text(f)        # generate text chunk
  141.  
  142.       }
  143. end
  144.  
  145. procedure htc_tag(f)
  146.    local s
  147.  
  148.    s := "<"
  149.    repeat {
  150.       if s ||:= tab(upto('>') + 1) then
  151.      return s            # completed tag
  152.       s ||:= tab(0)
  153.       &subject := (read(f) || "\n") | break
  154.       }
  155.    return s                # unclosed tag
  156. end
  157.  
  158. procedure htc_comment(f)
  159.    local s
  160.  
  161.    s := ""
  162.    repeat {
  163.       if s ||:= tab(find('-->') + 3) then
  164.      fail                # normal case: discard comment
  165.       s ||:= tab(0)
  166.       &subject := (read(f) || "\n") | break
  167.       }
  168.  
  169.    &subject := s            # rescan unclosed comment
  170.    return "<!--"            # return error indicator
  171. end
  172.  
  173. procedure htc_text(f)
  174.    local s
  175.  
  176.    s := ""
  177.    repeat {
  178.       if s ||:= tab(upto('<')) then
  179.      return s
  180.       s ||:= tab(0)
  181.       &subject := (read(f) || "\n") | return s
  182.       }
  183. end
  184.  
  185.  
  186. ##  htrefs(f) -- generate references from HTML file f
  187.  
  188. procedure htrefs(f)            #: generate references from HTML file
  189.    local tag, tagname, kwset, s
  190.    static ttable
  191.    initial {
  192.       ttable := table()
  193.       ttable["A"]    := set(["HREF"])
  194.       ttable["ARE"]    := set(["HREF"])
  195.       ttable["BASE"]    := set(["HREF"])
  196.       ttable["BODY"]    := set(["BACKGROUND"])
  197.       ttable["FORM"]    := set(["ACTION"])
  198.       ttable["IMG"]    := set(["SRC", "LOSRC", "USEMAP"])
  199.       ttable["INPUT"]    := set(["SRC"])
  200.       ttable["LINK"]    := set(["HREF"])
  201.       }
  202.  
  203.    every tag := htchunks(f) do {
  204.       tagname := httag(tag) | next
  205.       kwset := \ttable[tagname] | next
  206.       every s := htvals(tag) do
  207.      if member(kwset, s ? tab(upto(' '))) then
  208.         suspend tagname || " " || s
  209.       }
  210. end
  211.  
  212.  
  213.  
  214. ##  httag(s) -- return the name of the HTML tag s
  215.  
  216. procedure httag(s)            #: extract name of HTML tag
  217.    static idset, wset, lcase, ucase
  218.    initial {
  219.       idset := &letters ++ &digits ++ '.-'
  220.       wset := ' \t\r\n\v\f'
  221.       lcase := string(&lcase)
  222.       ucase := string(&ucase)
  223.    }
  224.  
  225.    s ? {
  226.       ="<" | fail
  227.       tab(many(wset))
  228.       return map(tab(many(idset)), lcase, ucase)
  229.    }
  230. end
  231.  
  232.  
  233.  
  234. ##  htvals(s) -- generate tag values of HTML tag s
  235.  
  236. procedure htvals(s)            #: generate values in HTML tag
  237.    local kw
  238.    static idset, wset, qset, lcase, ucase
  239.    initial {
  240.       idset := &letters ++ &digits ++ '.-'
  241.       wset := ' \t\r\n\v\f'
  242.       qset := wset ++ '>'
  243.       lcase := string(&lcase)
  244.       ucase := string(&ucase)
  245.    }
  246.  
  247.    s ? {
  248.       ="<" | fail
  249.       tab(many(wset))
  250.       tab(many(idset)) | fail        # no name
  251.       repeat {
  252.      tab(upto(idset)) | fail
  253.      kw := map(tab(many(idset)), lcase, ucase)
  254.      tab(many(wset))
  255.      if ="=" then {
  256.         tab(many(wset))
  257.         kw ||:= " "
  258.         if ="\"" then {
  259.            kw ||:= tab(upto('"') | 0)
  260.            tab(any('"'))
  261.            }
  262.         else if ="'" then {
  263.            kw ||:= tab(upto('\'') | 0)
  264.            tab(any('\''))
  265.            }
  266.         else
  267.            kw ||:= tab(upto(qset) | 0)
  268.         }
  269.      suspend kw
  270.       }
  271.    }
  272. end
  273.  
  274.  
  275.  
  276. #  urlmerge(base,new) -- merge URLs
  277.  
  278. procedure urlmerge(base, new)        #: merge URLs
  279.    local protocol, host, path
  280.    static notslash
  281.    initial notslash := ~'/'
  282.  
  283.    if new ? (tab(many(&letters)) & =":") then
  284.       return new            # new is fully specified
  285.  
  286.    base ? {
  287.       protocol := (tab(many(&letters)) || =":") | ""
  288.       host := (="//" || tab(upto('/') | 0)) | ""
  289.       path := tab(upto('#') | 0)
  290.       }
  291.  
  292.    new ? {
  293.       if ="#" then
  294.      return protocol || host || path || new
  295.       if ="/" then
  296.      return protocol || host || new
  297.  
  298.       while (="." & (="/" | pos(0))) |
  299.             (=".." & (="/" | pos(0)) & (path := url_trim(path)))
  300.  
  301.       return protocol || host || trim(path, notslash) || tab(0)
  302.       }
  303. end
  304.  
  305. #  url_trim(path) -- trim trailing dir provided that at least one "/" remains
  306.  
  307. procedure url_trim(path)
  308.    static notslash
  309.    initial notslash := ~'/'
  310.  
  311.    reverse(path) ? {            # work from back end
  312.       tab(upto('/') + 1) | fail        # trim dir, fail if no "/"
  313.       if =".." & (="/" | pos(0))
  314.      then fail            # don't trim a ".." component
  315.       path := reverse(tab(0))        # otherwise use the rest
  316.    }
  317.    if upto('/', path) then        # one / must remain to be valid
  318.       return path
  319.    else
  320.       fail
  321. end
  322.